import json
import os
import yaml
import re
import requests
from typing import Optional, Dict, Any
from pydantic import BaseModel, Field
from fastapi import (APIRouter, HTTPException)
from bisheng.utils.logger import logger
from bisheng.settings import settings
# 测试获取工作流配置，使用loguru
# from loguru import logger


# build router
router = APIRouter(prefix='/info_publish', tags=['OpenAPI', 'info_publish'])



class ExceptionMessage(BaseModel):
    input_id: Optional[str] = Field(default=None, description="异常信息id")
    exception_time: Optional[str] = Field(default=None, description="异常时间")
    exception_message: Optional[str]  = Field(default=None, description="异常消息")
    publish_message: Optional[str] = Field(default=None, description="发布消息")
    location: Optional[str] = Field(default=None, description="地点")
    first_report: Optional[bool] = Field(default=True, description="首报")
    stream: Optional[bool] = Field(default=False, description="流式传输，默认False")
    

@router.post("/send_exception_message")
async def send_exception_message(exception_message: ExceptionMessage):
    '''
    向毕晟信息发布智能体发送消息
    '''
    try:
        
        # workflow_config = _get_workflow_config()
        
        # 从settings获取配置
        info_publish_config = settings.info_publish
        base_url = info_publish_config.workflow_url
        port = info_publish_config.port
        workflow_id = info_publish_config.workflow_id
        callback_url = info_publish_config.callback_url
        workflow_url = f"{base_url}:{port}/api/v2/workflow/invoke"
        workflow_config = {
            'workflow_url': workflow_url,
            'workflow_id': workflow_id,
            'callback_url': callback_url
        }
        
        
        # 初始化工作流会话
        session_info = _initialize_workflow_session(workflow_config)
        
        # 执行工作流
        workflow_response = _execute_workflow(workflow_config, session_info, exception_message)
        
        # 处理工作流响应
        result = _process_workflow_response(workflow_response, workflow_config['callback_url'])
        
        logger.info(result)
        
        return result
        
    except Exception as e:
        logger.error(f"Exception occurred: {str(e)}")
        raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}")


def _get_workflow_config() -> Dict[str, Any]:
    """获取工作流配置信息"""
    base_url, port, workflow_id, callback_url = load_info_publish_config()
    workflow_url = f"{base_url}:{port}/api/v2/workflow/invoke"
    
    logger.info(f"workflow_url: {workflow_url}")
    logger.info(f"port: {port}")
    logger.info(f"callback_url: {callback_url}")
    logger.info(f"workflow_id: {workflow_id}")
    
    return {
        'workflow_url': workflow_url,
        'workflow_id': workflow_id,
        'callback_url': callback_url
    }


def _initialize_workflow_session(workflow_config: Dict[str, Any]) -> Dict[str, Any]:
    """初始化工作流会话，返回会话信息"""
    headers = {'Content-Type': 'application/json'}
    
    init_payload = {
        "workflow_id": workflow_config['workflow_id'],
        "stream": False
    }
    
    logger.info(f"初始化工作流: {workflow_config['workflow_id']}")
    init_response = requests.post(workflow_config['workflow_url'], headers=headers, json=init_payload)
    logger.debug(f"初始化工作流响应: {init_response.json()}")
    
    # 从响应中提取 session_id、node_id、message_id
    resp_json = init_response.json()
    session_id = resp_json.get('data', {}).get('session_id')
    events = resp_json.get('data', {}).get('events', [])
    
    input_node_id = None
    message_id = None
    for event in events:
        if event.get('event') == 'input':
            input_node_id = event.get('node_id')
            message_id = event.get('message_id')
            break
    
    logger.info(f"input_node_id: {input_node_id}, message_id: {message_id}")
    
    return {
        'session_id': session_id,
        'input_node_id': input_node_id,
        'message_id': message_id
    }


def _execute_workflow(workflow_config: Dict[str, Any], session_info: Dict[str, Any], 
                     exception_message: ExceptionMessage) -> requests.Response:
    """执行工作流，返回响应"""
    headers = {'Content-Type': 'application/json'}
    
    # 构建输入数据
    input_data = {
        f"{session_info['input_node_id']}": {
            "input_id": exception_message.input_id or "",
            "exception_time": exception_message.exception_time or "",
            "exception_info": exception_message.exception_message or "",
            "publish_info": exception_message.publish_message or "",
            "location": exception_message.location or "",
            "first_report": exception_message.first_report,
        }
    }
    
    payload_data = {
        "workflow_id": workflow_config['workflow_id'],
        "stream": exception_message.stream,
        "input": input_data,
        "message_id": session_info['message_id'],
        "session_id": session_info['session_id']
    }
    
    logger.info(f"payload_data: {payload_data}")
    response = requests.post(workflow_config['workflow_url'], headers=headers, json=payload_data)
    
    # 检查响应状态
    if response.status_code != 200:
        logger.error(f"工作流请求失败，状态码: {response.status_code}")
        logger.error(f"响应内容: {response.text}")
        raise HTTPException(status_code=response.status_code, detail=f"工作流请求失败: {response.text}")
    
    logger.debug(f"工作流响应: {response.json()}")
    return response


def _process_workflow_response(response: requests.Response, callback_url: str) -> Dict[str, Any]:
    """处理工作流响应，解析消息并执行回调"""
    try:
        resp_data = response.json()
        messages = _extract_message_from_response(resp_data)
        
        if not messages:
            logger.error("无法解析message内容")
            raise HTTPException(status_code=500, detail="无法解析返回的message内容")
        
        # 执行回调
        callback_results = _execute_callbacks_for_multiple_messages(messages, callback_url)
        
        return {
            "message": "消息处理成功", 
            "message_info": messages, 
            "callback_results": callback_results
        }
        
    except HTTPException as e:
        # 如果是我们自定义的HTTPException，直接重新抛出
        if isinstance(e.detail, dict) and "all_results" in e.detail:
            # 这是新的异常格式，包含所有结果
            return {
                "message": e.detail.get("message", "部分消息处理失败"),
                "message_info": messages if 'messages' in locals() else [],
                "callback_results": e.detail.get("all_results", []),
                "failures": e.detail.get("failures", []),
                "status": "partial_success" if e.detail.get("all_results") else "failed"
            }
        else:
            # 这是其他HTTPException，重新抛出
            raise e
    except Exception as e:
        logger.error(f"{str(e)}")
        raise HTTPException(status_code=500, detail=f"{str(e)}")


def _extract_message_from_response(resp_data: Dict[str, Any]) -> list[Dict[str, Any]]:
    """从工作流响应中提取所有output_msg事件的消息"""
    events = resp_data.get('data', {}).get('events', [])
    messages = []
    
    for event in events:
        logger.debug(f"工作流event:{event}")
        if event.get('event') == 'output_msg':
            output_schema = event.get('output_schema', {})
            logger.debug(f'工作流output_schema:{output_schema}')
            
            message_str = output_schema.get('message')
            logger.info(f"message_id-{event.get('message_id')}: {message_str}")
            
            # 使用灵活解析方法
            message = parse_flexible_json(message_str)
            logger.debug(f"message: {message}")
            
            if message:
                messages.append(message)
                logger.info(f"提取到消息: {message}")
    
    if messages:
        return messages
    
    logger.error("未能在响应中找到 message 字段")
    raise HTTPException(status_code=500, detail="未能在响应中找到 message 字段")


def _execute_callbacks_for_multiple_messages(messages: list[Dict[str, Any]], callback_url: str) -> list[Dict[str, Any]]:
    """为多个消息分别执行对应的回调函数"""
    callback_results = []
    has_failures = False
    failure_details = []
    
    for i, message in enumerate(messages):
        logger.info(f"处理第 {i+1} 条消息: {message}")
        
        try:
            # 根据消息内容决定调用哪个回调
            if 'classification_result' in message:
                # 执行分类结果回调
                resp_api = callback_api(callback_url, message)
                logger.info(f"第 {i+1} 条消息 callback_api返回: {resp_api}")
                
                if not resp_api or not resp_api.get("success", False):
                    logger.error(f"第 {i+1} 条消息 callback_api回调失败: {resp_api}")
                    has_failures = True
                    failure_details.append(f"第 {i+1} 条消息 callback_api回调失败: {resp_api}")
                    callback_results.append({
                        'message_index': i+1,
                        'message_type': 'classification_result',
                        'callback_type': 'api',
                        'result': resp_api,
                        'status': 'failed'
                    })
                else:
                    callback_results.append({
                        'message_index': i+1,
                        'message_type': 'classification_result',
                        'callback_type': 'api',
                        'result': resp_api,
                        'status': 'success'
                    })
                
            elif 'reconstruct_publish_message' in message:
                # 执行重构发布消息回调
                resp_raw = callback_raw_api(callback_url, message)
                logger.info(f"第 {i+1} 条消息 callback_raw_api返回: {resp_raw}")
                
                if not resp_raw or not resp_raw.get("success", False):
                    logger.error(f"第 {i+1} 条消息 callback_raw_api回调失败: {resp_raw}")
                    has_failures = True
                    failure_details.append(f"第 {i+1} 条消息 callback_raw_api回调失败: {resp_raw}")
                    callback_results.append({
                        'message_index': i+1,
                        'message_type': 'reconstruct_publish_message',
                        'callback_type': 'raw_api',
                        'result': resp_raw,
                        'status': 'failed'
                    })
                else:
                    callback_results.append({
                        'message_index': i+1,
                        'message_type': 'reconstruct_publish_message',
                        'callback_type': 'raw_api',
                        'result': resp_raw,
                        'status': 'success'
                    })
            else:
                logger.warning(f"第 {i+1} 条消息未找到匹配的回调类型: {message}")
                callback_results.append({
                    'message_index': i+1,
                    'message_type': 'unknown',
                    'callback_type': 'none',
                    'result': None,
                    'status': 'skipped'
                })
                
        except Exception as e:
            logger.error(f"第 {i+1} 条消息处理异常: {str(e)}")
            has_failures = True
            failure_details.append(f"第 {i+1} 条消息处理异常: {str(e)}")
            callback_results.append({
                'message_index': i+1,
                'message_type': 'unknown',
                'callback_type': 'none',
                'result': {'error': str(e)},
                'status': 'error'
            })
    
    # 如果有失败的情况，抛出包含所有结果的异常
    if has_failures:
        raise HTTPException(
            status_code=500, 
            detail={
                "message": "部分消息回调失败",
                "failures": failure_details,
                "all_results": callback_results
            }
        )
    
    return callback_results


def _execute_callbacks(message: Dict[str, Any], callback_url: str) -> Dict[str, Any]:
    """执行回调函数（保留原函数以兼容性）"""
    results = {'api': None, 'raw': None}
    
    # 执行分类结果回调
    if 'classification_result' in message:
        resp_api = callback_api(callback_url, message)
        logger.info(f"callback_api返回: {resp_api}")
        
        if not resp_api or not resp_api.get("success", False):
            logger.error(f"callback_api回调失败: {resp_api}")
            raise HTTPException(status_code=500, detail=f"callback_api回调失败: {resp_api}")
        
        results['api'] = resp_api
    
    # 执行重构发布消息回调
    if 'reconstruct_publish_message' in message:
        resp_raw = callback_raw_api(callback_url, message)
        logger.info(f"callback_raw_api返回: {resp_raw}")
        
        if not resp_raw or not resp_raw.get("success", False):
            logger.error(f"callback_raw_api回调失败: {resp_raw}")
            raise HTTPException(status_code=500, detail=f"callback_raw_api回调失败: {resp_raw}")
        
        results['raw'] = resp_raw
    
    return results


# 灵活解析JSON字符串，通过提取键值对重新构建JSON对象
def parse_flexible_json(json_str: str) -> Optional[Dict[str, Any]]:
    """
    灵活解析JSON字符串，通过提取键值对重新构建JSON对象
    
    Args:
        json_str: 要解析的JSON字符串
        
    Returns:
        解析后的JSON对象，如果解析失败返回None
    """
    if not json_str or not json_str.strip():
        return None
    
    # 1. 清理字符串
    cleaned_str = json_str.strip()
    
    # 2. 尝试直接解析（优先尝试标准JSON）
    try:
        result = json.loads(cleaned_str)
        # 后处理：检查并解析字符串中的Python字典
        return _post_process_string_dicts(result)
    except json.JSONDecodeError:
        pass
    
    # 3. 尝试解析Python字典字符串
    try:
        if cleaned_str.startswith('{') and cleaned_str.endswith('}'):
            python_dict_str = cleaned_str.replace("'", '"')
            result = json.loads(python_dict_str)
            return _post_process_string_dicts(result)
    except json.JSONDecodeError:
        pass
    
    # 4. 使用正则表达式提取键值对
    result_dict = {}
    
    # 更精确的JSON键值对匹配模式
    # 匹配格式: "key": value 或 key: value，确保是JSON结构中的键值对
    
    # 匹配字符串值 - 更精确的模式
    # 确保键名后面有冒号，值在引号内
    string_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*["\']([^"\']*)["\']'
    string_matches = re.findall(string_pattern, cleaned_str)
    for key, value in string_matches:
        # 检查值是否是Python字典字符串格式
        if value.startswith('{') and value.endswith('}'):
            try:
                # 尝试解析为Python字典
                python_dict_str = value.replace("'", '"')
                parsed_dict = json.loads(python_dict_str)
                result_dict[key] = parsed_dict
            except json.JSONDecodeError:
                # 如果解析失败，保持原始字符串
                result_dict[key] = value
        else:
            result_dict[key] = value
    
    # 匹配数字值 - 更精确的模式
    # 确保键名后面有冒号，值是独立的数字
    number_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*(-?\d+\.?\d*)\s*[,}\]]'
    number_matches = re.findall(number_pattern, cleaned_str)
    for key, value in number_matches:
        try:
            if '.' in value:
                result_dict[key] = float(value)
            else:
                result_dict[key] = int(value)
        except ValueError:
            result_dict[key] = value
    
    # 匹配布尔值 - 更精确的模式
    bool_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*(true|false|True|False)\s*[,}\]]'
    bool_matches = re.findall(bool_pattern, cleaned_str)
    for key, value in bool_matches:
        result_dict[key] = value.lower() == 'true'
    
    # 匹配null值 - 更精确的模式
    null_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*(null|None)\s*[,}\]]'
    null_matches = re.findall(null_pattern, cleaned_str)
    for key, value in null_matches:
        result_dict[key] = None
    
    # 匹配嵌套对象 - 更精确的模式
    # 使用递归匹配，确保是完整的对象结构
    object_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*\{'
    object_matches = re.findall(object_pattern, cleaned_str)
    for key in object_matches:
        # 找到对应的结束括号
        start_pos = cleaned_str.find(f'"{key}": {{')
        if start_pos == -1:
            start_pos = cleaned_str.find(f"'{key}': {{")
        
        if start_pos != -1:
            # 找到匹配的结束括号
            brace_count = 0
            end_pos = start_pos
            for i in range(start_pos, len(cleaned_str)):
                if cleaned_str[i] == '{':
                    brace_count += 1
                elif cleaned_str[i] == '}':
                    brace_count -= 1
                    if brace_count == 0:
                        end_pos = i
                        break
            
            if end_pos > start_pos:
                nested_str = cleaned_str[start_pos:end_pos+1]
                # 提取嵌套对象的内容
                nested_content = re.search(r'\{.*\}', nested_str, re.DOTALL)
                if nested_content:
                    nested_obj = nested_content.group(0)
                    try:
                        nested_dict = parse_flexible_json(nested_obj)
                        if nested_dict and not nested_dict.get('parse_error'):
                            result_dict[key] = nested_dict
                        else:
                            result_dict[key] = nested_obj
                    except:
                        result_dict[key] = nested_obj
    
    # 匹配数组 - 更精确的模式
    array_pattern = r'["\']([^"\':\s,{}[\]]+)["\']\s*:\s*\['
    array_matches = re.findall(array_pattern, cleaned_str)
    for key in array_matches:
        # 找到对应的结束方括号
        start_pos = cleaned_str.find(f'"{key}": [')
        if start_pos == -1:
            start_pos = cleaned_str.find(f"'{key}': [")
        
        if start_pos != -1:
            # 找到匹配的结束方括号
            bracket_count = 0
            end_pos = start_pos
            for i in range(start_pos, len(cleaned_str)):
                if cleaned_str[i] == '[':
                    bracket_count += 1
                elif cleaned_str[i] == ']':
                    bracket_count -= 1
                    if bracket_count == 0:
                        end_pos = i
                        break
            
            if end_pos > start_pos:
                array_str = cleaned_str[start_pos:end_pos+1]
                # 提取数组内容
                array_content = re.search(r'\[.*\]', array_str, re.DOTALL)
                if array_content:
                    array_content_str = array_content.group(0)
                    try:
                        # 尝试解析数组
                        if array_content_str == '[]':
                            result_dict[key] = []
                        else:
                            # 简单分割数组元素
                            elements = []
                            # 移除首尾的方括号
                            content = array_content_str[1:-1].strip()
                            if content:
                                # 按逗号分割，但要注意字符串内的逗号
                                parts = re.split(r',(?=(?:[^"]*"[^"]*")*[^"]*$)', content)
                                for part in parts:
                                    part = part.strip().strip('"\'')
                                    if part:
                                        elements.append(part)
                            result_dict[key] = elements
                    except:
                        result_dict[key] = array_content_str
    
    # 5. 如果提取到了键值对，返回结果
    if result_dict:
        result_dict = _post_process_string_dicts(result_dict)
        logger.info(f"通过键值对提取成功解析JSON: {result_dict}")
        return result_dict
    
    # 6. 如果所有方法都失败，返回原始内容
    logger.warning(f"无法解析JSON字符串，返回原始内容: {json_str[:100]}...")
    return {"raw_message": json_str, "parse_error": True}


def _post_process_string_dicts(data: Any) -> Any:
    """
    后处理函数，递归检查并解析字符串中的Python字典
    
    Args:
        data: 要处理的数据
        
    Returns:
        处理后的数据
    """
    if isinstance(data, dict):
        result = {}
        for key, value in data.items():
            result[key] = _post_process_string_dicts(value)
        return result
    elif isinstance(data, list):
        return [_post_process_string_dicts(item) for item in data]
    elif isinstance(data, str):
        # 检查字符串是否是Python字典格式
        if data.startswith('{') and data.endswith('}'):
            try:
                # 尝试解析为Python字典
                python_dict_str = data.replace("'", '"')
                parsed_dict = json.loads(python_dict_str)
                return parsed_dict
            except json.JSONDecodeError:
                # 如果解析失败，保持原始字符串
                return data
        else:
            return data
    else:
        return data

def load_info_publish_config():
            """
            读取 info_publish 配置，跳过 !env 标签，返回相关配置项
            :return: (base_url, port, workflow_id, callback_url)
            :raises: HTTPException
            """
            # 配置文件路径
            config_path = os.path.abspath(
                os.path.join(os.path.dirname(__file__), '../../../../..', 'docker/bisheng/config/config.yaml')
            )

            if not os.path.exists(config_path):
                raise HTTPException(status_code=500, detail=f"配置文件不存在: {config_path}")

            try:
                # 读取文件内容
                with open(config_path, 'r', encoding='utf-8') as f:
                    content = f.read()

                # 移除所有!env标签，保留值
                content_clean = re.sub(r'!env\s+', '', content)

                # 解析YAML
                config = yaml.safe_load(content_clean)

                # 提取info_publish配置
                if not config or 'info_publish' not in config:
                    raise HTTPException(status_code=500, detail="配置文件中未找到info_publish配置")

                info_config = config['info_publish']
                base_url = info_config.get('workflow_url')
                port = info_config.get('port')
                workflow_id = info_config.get('workflow_id')
                callback_url = info_config.get('callback_url')

                if not base_url or not port or not workflow_id or not callback_url:
                    raise HTTPException(status_code=500, detail="配置文件中缺少必要的配置项: workflow_url, port, workflow_id, callback_url")

                logger.info(f"从配置文件读取配置: base_url={base_url}, port={port}, workflow_id={workflow_id}")

                return base_url, port, workflow_id, callback_url

            except Exception as e:
                logger.error(f"读取配置文件失败: {str(e)}")
                raise HTTPException(status_code=500, detail=f"读取配置文件失败: {str(e)}")
   
def callback_api(callback_url, message):
    url = f"{callback_url}/api/v1/messages"
    data = {"content": message}
    headers = {
        'Content-Type': 'application/json'
    }
    try:
        logger.debug(f"callback_api请求URL: {url}")
        logger.debug(f"callback_api请求体: {data}")
        response = requests.post(url, json=data, headers=headers, timeout=10)
        logger.debug(f"callback_api response请求URL: {response.url}")
        logger.debug(f"callback_api response请求体: {response.request.body}")
        response.raise_for_status()
        try:
            resp_json = response.json()
            logger.info(f"callback_api响应: {resp_json}")
            return resp_json
        except Exception as e:
            logger.error(f"callback_api响应解析失败: {str(e)}，原始响应: {response.text}")
            return {"error": "响应解析失败", "detail": str(e), "raw_response": response.text}
    except requests.RequestException as e:
        logger.error(f"callback_api请求异常: {str(e)}")
        return {"error": "请求异常", "detail": str(e)}

def callback_raw_api(callback_url, message):
    """发布原始消息"""
    url = f"{callback_url}/api/v1/messages/raw"
    headers = {
        'Content-Type': 'application/json'
    }
    try:
        logger.debug(f"callback_raw_api请求URL: {url}")
        logger.debug(f"callback_raw_api请求体: {message}")
        logger.debug(f"callback_raw_api请求头: {headers}")
        response = requests.post(url, json=message, headers=headers, timeout=10)
        logger.debug(f"callback_raw_api response请求URL: {response.url}")
        logger.debug(f"callback_raw_api response请求体: {response.request.body}")
        response.raise_for_status()
        try:
            resp_json = response.json()
            logger.info(f"callback_raw_api响应: {resp_json}")
            return resp_json
        except Exception as e:
            logger.error(f"callback_raw_api响应解析失败: {str(e)}，原始响应: {response.text}")
            return {"error": "响应解析失败", "detail": str(e), "raw_response": response.text}
    except requests.RequestException as e:
        logger.error(f"callback_raw_api请求异常: {str(e)}")
        return {"error": "请求异常", "detail": str(e)}
    
if __name__ == "__main__":
    import asyncio
    
    
    async def main():
        exception_message = ExceptionMessage(
            input_id="1234567890",
            exception_time="2025-07-28 10:00:00",
            exception_message="2号线信息：06:15，浮山所站上行站台整侧PIS不亮，专业人员正在现场处理。",
            publish_message="2号线信息：06:15，浮山所站上行站台整侧PIS不亮，专业人员正在现场处理。",
            location="2号线信息",
            first_report=True,
            stream=False
        )
        await send_exception_message(exception_message)
    
    asyncio.run(main())